К средствам логического преобразования данных относятся
логические команды и логические
операции.
В связи с последним напомню, что операнд команды ассемблера
в общем случае может представлять собой выражение, которое, в свою очередь,
является комбинаций операторов и операндов. Среди этих операторов могут
быть и операторы, реализующие логические операции над объектами выражения.
Перед подробным рассмотрением этих средств давайте посмотрим, что же представляют собой сами логические данные и какие операции над ними производятся.
Исчисление высказываний очень гармонично сочетается с принципами работы компьютера и основными методами его программирования. Все аппаратные компоненты компьютера построены на логических микросхемах. Система представления информации в компьютере на самом нижнем уровне основана на понятии бита. Бит, имея всего два состояния: 0 (ложно) и 1 (истинно), — естественным образом вписывается в исчисление высказываний.
Согласно теории, над высказываниями (над битами) могут выполняться следующие логические операции:
Таблица 1. Таблица истинности для логического отрицания
Значение операнда | 0 | 1 |
Результат операции | 1 | 0 |
логическое сложение (логическое включающее ИЛИ) — логическая операция над двумя операндами, результатом которой является “истина” (1), если один или оба операнда имеют значение “истина” (1), и “ложь” (0) — если оба операнда имеют значение “ложь” (0).
Таблица 2. Таблица истинности для логического включающего ИЛИ
Значение операнда 1 | 0 | 0 | 1 | 1 |
Значение операнда 2 | 0 | 1 | 0 | 1 |
Результат операции | 0 | 1 | 1 | 1 |
логическое умножение (логическое И) — логическая операция над двумя операндами, результатом которой является “истина” (1) только в том случае, если оба операнда имеют значение “истина” (1). Во всех остальных случаях значение операции “ложь” (0).
Таблица 3. Таблица истинности для логического И
Значение операнда 1 | 0 | 0 | 1 | 1 |
Значение операнда 2 | 0 | 1 | 0 | 1 |
Результат операции | 0 | 0 | 0 | 1 |
логическое исключающее сложение (логическое исключающее ИЛИ) — логическая операция над двумя операндами, результатом которой является “истина” (1), если только один из двух операндов имеет значение “истина” (1), и ложь (0), если оба операнда имеют значение “ложь” (0) или “истина” (1).
Таблица 4. Таблица истинности для логического исключающего ИЛИ
Значение операнда 1 | 0 | 0 | 1 | 1 |
Значение операнда 2 | 0 | 1 | 0 | 1 |
Результат операции | 0 | 1 | 1 | 0 |
Система команд микропроцессора содержит пять команд, поддерживающих данные операции. Эти команды выполняют логические операции над битами операндов. Размерность операндов, естественно, должна быть одинакова. Например, если размерность операндов равна слову (16 бит), то логическая операция выполняется сначала над нулевыми битами операндов и ее результат записывается на место бита 0 результата. Далее команда последовательно повторяет эти действия над всеми битами с первого до пятнадцатого. Возможные варианты размерности операндов для каждой команды приведены в “Справочнике команд”.
and
операнд_1,операнд_2 — операция логического умножения.
Команда выполняет поразрядно логическую операцию И (конъюнкцию)
над битами операндов операнд_1 и операнд_2. Результат записывается на место
операнд_1.
or
операнд_1,операнд_2 — операция логического сложения.
Команда выполняет поразрядно логическую операцию ИЛИ
(дизъюнкцию) над битами операндов операнд_1 и операнд_2. Результат записывается
на место операнд_1.
xor
операнд_1,операнд_2 — операция логического исключающего сложения.
Команда выполняет поразрядно логическую операцию исключающего
ИЛИ над битами операндов операнд_1 и операнд_2. Результат записывается
на место операнд_1.
test
операнд_1,операнд_2 — операция “проверить” (способом логического
умножения).
Команда выполняет поразрядно логическую операцию И над
битами операндов операнд_1 и операнд_2. Состояние операндов остается прежним,
изменяются только флаги zf, sf, и pf, что дает возможность анализировать
состояние отдельных битов операнда без изменения их состояния.
not
операнд — операция логического отрицания.
Команда выполняет поразрядное инвертирование (замену
значения на обратное) каждого бита операнда. Результат записывается на
место операнда.
Для представления роли логических команд в системе команд
микропроцессора очень важно понять области их применения и типовые приемы
их использования при программировании.
С помощью логических команд возможно выделение
отдельных битов в операнде с целью их установки, сброса, инвертирования
или просто проверки на определенное значение.
Для организации подобной работы с битами операнд_2
обычно играет роль маски. С помощью установленных в 1 битов этой
маски и определяются нужные для конкретной операции биты операнд_1. Покажем,
какие логические команды могут применяться для этой цели:
or eax,10b ;установить 1-й бит в регистре eax |
and eax,fffffffdh ;сбросить в 0 1-й бит в регистре eax |
xor eax,10b ;инвертировать 1-й бит в регистре eax jz mes ;переход, если 1-й бит в al был единичным |
Интересующие нас биты маски (операнд_2) при выполнении команды xor должны быть единичными, остальные — нулевыми.
test eax,00000010h jz m1 ;переход, если 4-й бит равен 1 |
Как видно из примера, для реакции на результат команды test целесообразно использовать команду перехода jnz метка (Jump if Not Zero) — переход, если флаг нуля zf ненулевой, или команду с обратным действием — jz метка (Jump if Zero) — переход, если флаг нуля zf = 0.
Следующие две команды позволяют осуществить поиск первого установленного в 1 бита операнда. Поиск можно произвести как с начала так и от конца операнда:
bsf
операнд_1,операнд_2 (Bit Scaning Forward) - сканирование битов вперед.
Команда просматривает (сканирует) биты операнд_2 от младшего
к старшему (от бита 0 до старшего бита) в поисках первого бита, установленного
в 1. Если таковой обнаруживается, в операнд_1 заносится номер этого бита
в виде целочисленного значения. Если все биты операнд_2 равны 0, то флаг
нуля zf устанавливается в 1, в противном случае флаг zf сбрасывается в
0.
mov al,02h bsf bx,al ;bx=1 jz m1 ;переход, если al=00h ... |
Листинг 1 демонстрирует пример применения команд bsr и bsf. Введите код и исследуйте работу программы в отладчике (в частности, обратите внимание на то, как меняется содержимое регистра bx после команд bsf и bsr).
Листинг 1 Сканирование битов ;prg_9_1.asm masm model small stack 256 .data ;сегмент данных .code ;сегмент кода main: ;точка входа в программу mov ax,@data mov ds,ax ;... .486 ;это обязательно xor ax,ax mov al,02h bsf bx,ax ;bx=1 jz m1 ;переход, если al=00h bsr bx,ax m1: ;... mov ax,4c00h ;стандартный выход int 21h end main |
В последних моделях микропроцессоров Intel в группе логических команд появилось еще несколько команд, которые позволяют осуществить доступ к одному конкретному биту операнда. Операнд может находиться как в памяти, так и в регистре общего назначения. Положение бита задается смещением бита относительно младшего бита операнда. Значение смещения может задаваться как в виде непосредственного значения, так и содержаться в регистре общего назначения. В качестве значения смещения вы можете использовать результаты работы команд bsr и bsf. Все команды присваивают значение выбранного бита флагу cf.
bt
операнд,смещение_бита (Bit Test) — проверка бита.
Команда переносит значение бита в флаг cf.
bt ax,5 ;проверить значение бита 5 jnc m1 ;переход, если бит = 0 |
mov ax,10 bts pole,ax ;проверить и установить 10-й бит в pole jсm1 ;переход, если проверяемый бит был равен 1 |
btc
операнд,смещение_бита (Bit Test and Convert) — проверка и инвертирование
бита.
Команда переносит значение бита в флаг cf и затем инвертирует
значение этого бита.
коп операнд,счетчик_сдвигов
Количество сдвигаемых разрядов — счетчик_сдвигов — располагается, как видите, на месте второго операнда и может задаваться двумя способами:
Исходя из размерности регистра cl, понятно, что значение
счетчика сдвигов может лежать в диапазоне от 0 до 255. Но на самом деле
это не совсем так.
В целях оптимизации микропроцессор воспринимает только
значение пяти младших битов счетчика, то есть значение лежит в диапазоне
от 0 до 31.
В последних моделях микропроцессора, в том числе и в
микропроцессоре Pentium, есть дополнительные команды, позволяющие делать
64-разрядные сдвиги. Мы их рассмотрим чуть позже.
Все команды сдвига устанавливают флаг переноса cf.
По мере сдвига битов за пределы операнда они сначала
попадают на флаг переноса, устанавливая его равным значению очередного
бита, оказавшегося за пределами операнда. Куда этот бит попадет дальше,
зависит от типа команды сдвига и алгоритма программы.
По принципу действия команды сдвига можно разделить на два типа:
К командам логического линейного сдвига относятся следующие:
shl
операнд,счетчик_сдвигов (Shift Logical Left) - логический сдвиг
влево.
Содержимое операнда сдвигается влево на количество битов,
определяемое значением счетчик_сдвигов. Справа (в позицию младшего бита)
вписываются нули;
shr
операнд,счетчик_сдвигов (Shift Logical Right) — логический сдвиг
вправо.
Содержимое операнда сдвигается вправо на количество битов,
определяемое значением счетчик_сдвигов. Слева (в позицию старшего, знакового
бита) вписываются нули. На рис. 1 показан принцип работы этих команд.
Ниже показан фрагмент программы, который выполняет преобразование двух неупакованных BCD-чисел в слове памяти bcd_dig в упакованное BCD-число в регистре al.
... bcd_dig dw 0905h ;описание неупакованного BCD-числа 95 ... mov ax,bcd_dig ;пересылка shl ah,4 ;сдвиг влево add al,ah ;сложение для получения результата: al=95h |
sal
операнд,счетчик_сдвигов (Shift Arithmetic Left) —
арифметический сдвиг влево.
Содержимое операнда сдвигается влево на количество битов,
определяемое значением счетчик_сдвигов. Справа (в позицию младшего
бита) вписываются нули. Команда sal не сохраняет знака, но устанавливает
флаг cf в случае смены знака очередным выдвигаемым битом. В остальном
команда sal полностью аналогична команде shl;
sar
операнд,счетчик_сдвигов (Shift Arithmetic Right) — арифметический
сдвиг вправо.
Содержимое операнда сдвигается вправо на количество битов,
определяемое значением счетчик_сдвигов. Слева в операнд вписываются
нули. Команда sar сохраняет знак, восстанавливая его после сдвига
каждого очередного бита.
На рис. 2 показан принцип работы команд линейного арифметического
сдвига.
rol
операнд,счетчик_сдвигов (Rotate Left) — циклический
сдвиг влево.
Содержимое операнда сдвигается влево на количество бит,
определяемое операндом счетчик_сдвигов. Сдвигаемые влево биты записываются
в тот же операнд справа.
ror
операнд,счетчик_сдвигов (Rotate Right) — циклический сдвиг вправо.
Содержимое операнда сдвигается вправо на количество бит,
определяемое операндом счетчик_сдвигов. Сдвигаемые вправо биты записываются
в тот же операнд слева.
Как видно из рис. 3, команды простого циклического сдвига
в процессе своей работы осуществляют одно полезное действие, а именно:
циклически сдвигаемый бит не только вдвигается в операнд с другого конца,
но и одновременно его значение становиться значением флага cf.
К примеру, для того чтобы обменять содержимое двух половинок
регистра eax, достаточно выполнить следующую последовательность команд:
... mov eax,ffff0000h mov cl,16 rol eax,cl |
К командам циклического сдвига через флаг переноса cf относятся следующие:
rcl
операнд,счетчик_сдвигов (Rotate through Carry Left) — циклический
сдвиг влево через перенос.
Содержимое операнда сдвигается влево на количество бит,
определяемое операндом счетчик_сдвигов. Сдвигаемые биты поочередно
становятся значением флага переноса cf.
rcr
операнд,счетчик_сдвигов (Rotate through Carry Right) — циклический
сдвиг вправо через перенос.
Содержимое операнда сдвигается вправо на количество бит,
определяемое операндом счетчик_сдвигов. Сдвигаемые биты поочередно
становятся значением флага переноса cf.
Из рис. 4 видно, что при сдвиге через флаг переноса появляется
промежуточный элемент, с помощью которого, в частности, можно производить
подмену циклически сдвигаемых битов, в частности, рассогласование
битовых последовательностей.
Под рассогласованием битовой последовательности здесь
и далее подразумевается действие, которое позволяет некоторым образом локализовать
и извлечь нужные участки этой последовательности и записать их в другое
место.
shld
операнд_1,операнд_2,счетчик_сдвигов — сдвиг влево двойной точности.
Команда shld производит замену путем сдвига битов операнда
операнд_1 влево, заполняя его биты справа значениями битов, вытесняемых
из операнд_2 согласно схеме на рис. 5. Количество сдвигаемых бит определяется
значением счетчик_сдвигов, которое может лежать в диапазоне 0...31.
Это значение может задаваться непосредственным операндом или содержаться
в регистре cl. Значение операнд_2 не изменяется.
операнд_1,операнд_2,счетчик_сдвигов — сдвиг вправо двойной точности.
Как мы отметили, команды shld и shrd осуществляют сдвиги
до 32 разрядов, но за счет особенностей задания операндов и алгоритма работы
эти команды можно использовать для работы с полями длиной до 64 бит.
Например, рассмотрим, как можно осуществить сдвиг влево
на 16 бит поля из 64 бит.
;... .data pole_l dd 0b21187f5h1 pole_h dd 45ff6711h .code ;... .386 mov cl,16 ;загрузка счетчика сдвига в cl mov eax,pole_h shld pole_l,eax,cl shl pole_h,cl ;pole_l=87f50000h, pole_h=6711b211h |